#ifndef CSVPARSER_H_
#define CSVPARSER_H_

#include <fstream>
#include <list>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/lexical_cast.hpp>

#include "Defines.h"

/*-----------------------------------------------------------------------------
 *  Tipos
 *-----------------------------------------------------------------------------*/
#define CSV_IN  1   // Input
#define CSV_OUT 2   // Output

typedef boost::shared_ptr<ifstream> ifstreamPtr;
typedef boost::shared_ptr<ofstream> ofstreamPtr;

typedef list<double> ldouble;
typedef boost::shared_ptr<ldouble> ldoublePtr;

/*
 * =====================================================================================
 *        Class:  CSVFile
 *  Description:  Classe que faz a interface entre outras classes e os arquivos CSV
 * =====================================================================================
 */
class CSVFile
{

public:

    /*-----------------------------------------------------------------------------
     *  Contrói uma interface para um arquivo CSV passando seu nome e
     *  o tipo (input ou output)
     *-----------------------------------------------------------------------------*/
    CSVFile(const char *filename, int type);

    /*-----------------------------------------------------------------------------
     *  Retorna verdadeiro se tiver dados a serem lidos
     *-----------------------------------------------------------------------------*/
    bool has();

    /*-----------------------------------------------------------------------------
     *  Lê uma lista de valores do buffer
     *-----------------------------------------------------------------------------*/
    template<typename T, typename ...P>
    void read(T& t, P& ...p);

    /*-----------------------------------------------------------------------------
     *  Lê um valor do buffer
     *-----------------------------------------------------------------------------*/
    template<typename T>
    void read(T& t);

    /*-----------------------------------------------------------------------------
     *  Lê um vetor de valores do buffer
     *-----------------------------------------------------------------------------*/
    template<typename T>
    void readV(vector<T>& values, uint size);

    /*-----------------------------------------------------------------------------
     *  Escreve uma lista de valores no arquivo
     *-----------------------------------------------------------------------------*/
    template<typename T, typename ...P>
    void write(T& t, P& ...p);

    /*-----------------------------------------------------------------------------
     *  Escreve um valor no arquivo
     *-----------------------------------------------------------------------------*/
    template<typename T>
    void write(T& t);

    /*-----------------------------------------------------------------------------
     *  Escreve um vetor de valores no arquivo
     *-----------------------------------------------------------------------------*/
    template<typename T>
    void writeV(vector<T>& values, bool endLine = false);


private:

    /*-----------------------------------------------------------------------------
     *  Recupera mais dados para o buffer
     *-----------------------------------------------------------------------------*/
    void getMore();

    /*-----------------------------------------------------------------------------
     *  Tira um valor double do buffer
     *-----------------------------------------------------------------------------*/
    double pop();

    /*-----------------------------------------------------------------------------
     *  Escreve uma vírgula, se necessário
     *-----------------------------------------------------------------------------*/
    void writeComma();

    /*-----------------------------------------------------------------------------
     *  Tipo
     *-----------------------------------------------------------------------------*/
    int type;

    /*-----------------------------------------------------------------------------
     *  Indica início de uma linha (Output)
     *-----------------------------------------------------------------------------*/
    bool bol;

    /*-----------------------------------------------------------------------------
     *  Stream de entrada
     *-----------------------------------------------------------------------------*/
    ifstreamPtr in;

    /*-----------------------------------------------------------------------------
     *  Stream de saída
     *-----------------------------------------------------------------------------*/
    ofstreamPtr out;

    /*-----------------------------------------------------------------------------
     *  Buffer de dados
     *-----------------------------------------------------------------------------*/
    ldoublePtr buffer;

};

/*-----------------------------------------------------------------------------
 *  Ponteiro
 *-----------------------------------------------------------------------------*/
typedef boost::shared_ptr<CSVFile> CSVFilePtr;

/*
 *--------------------------------------------------------------------------------------
 *       Class:  CSVFile
 *      Method:  read
 * Description:  Lê uma lista de valores do buffer
 *--------------------------------------------------------------------------------------
 */
template<typename T, typename ...P>
void CSVFile::read(T& t, P& ...p)
{
//    assert(buffer->size() > 1);
    t = lexical_cast<T>(pop());
    read(p...);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  CSVFile
 *      Method:  read
 * Description:  Lê um valor do buffer
 *--------------------------------------------------------------------------------------
 */
template<typename T>
void CSVFile::read(T& t)
{
//    assert(buffer->size() == 1);
    t = lexical_cast<T>(pop());
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  CSVFile
 *      Method:  read
 * Description:  Lê vetor de valores do buffer
 *--------------------------------------------------------------------------------------
 */
template<typename T>
void CSVFile::readV(vector<T>& values, uint size)
{
//    assert(buffer->size() == size);
    for(uint i = 0; i < size; i++)
        values.push_back(lexical_cast<T>(pop()));
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  CSVFile
 *      Method:  write
 * Description:  Escreve uma lista de valores no arquivo
 *--------------------------------------------------------------------------------------
 */
template<typename T, typename ...P>
void CSVFile::write(T& t, P& ...p)
{
    writeComma();
    (*out) << t;
    write(p...);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  CSVFile
 *      Method:  write
 * Description:  Escreve um valor no arquivo
 *--------------------------------------------------------------------------------------
 */
template<typename T>
void CSVFile::write(T& t)
{
    writeComma();
    (*out) << t << endl;
    bol = true;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  CSVFile
 *      Method:  write
 * Description:  Escreve um vetor de valores no arquivo
 *--------------------------------------------------------------------------------------
 */
template<typename T>
void CSVFile::writeV(vector<T>& values, bool endLine)
{
    for(uint i = 0; i < values.size(); i++)
    {
        writeComma();
        (*out) << values[i];
    }
    if(endLine)
    {
        (*out) << endl;
        bol = true;
    }
}

#endif /* CSVPARSER_H_ */
